Skip to content

Fixed minimizer behaviour on exceeded iterations#231

Open
rozyczko wants to merge 9 commits intodevelopfrom
consistent_minimizers
Open

Fixed minimizer behaviour on exceeded iterations#231
rozyczko wants to merge 9 commits intodevelopfrom
consistent_minimizers

Conversation

@rozyczko
Copy link
Copy Markdown
Member

@rozyczko rozyczko commented Apr 9, 2026

This adds improvements to the fitting result reporting and evaluation tracking across all minimizer engines, ensuring that the number of function evaluations and relevant messages are consistently captured and propagated.

  • All minimizer engines (Bumps, DFO, LMFit) now populate n_evaluations (number of function evaluations) and message (status or error message) fields in FitResults, and these fields are propagated in multi-dataset fitting.

  • The Bumps minimizer uses a new _EvalCounter wrapper to count function evaluations and detect when the maximum evaluation budget is reached, updating the success flag and message accordingly.

  • The DFO minimizer now raises FitError only for real failures, and considers both success and max function evaluation warnings as successful fits, improving error reporting and testability.

@rozyczko rozyczko added [scope] enhancement Adds/improves features (major.MINOR.patch) [priority] medium Normal/default priority labels Apr 9, 2026
Copy link
Copy Markdown
Member

@henrikjacobsenfys henrikjacobsenfys left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me apart from a tiny nitpick.

This may also be a good opportunity to make a __repr__ method for the FitResults class?

Comment thread tests/integration/fitting/test_fitter.py
@rozyczko
Copy link
Copy Markdown
Member Author

rozyczko commented Apr 13, 2026

This may also be a good opportunity to make a __repr__ method for the FitResults class?

Good idea!
Added these

   lines = [
        f"FitResults(success={self.success}",
        f"  n_pars={self.n_pars}, n_points={len(self.x)}",
        f"  chi2={self.chi2:.4g}, reduced_chi={self.reduced_chi:.4g}",
        f"  n_evaluations={self.n_evaluations}",
        f"  minimizer={self.minimizer_engine.__name__ if self.minimizer_engine else None}",
    ]

Comment thread src/easyscience/fitting/minimizers/utils.py Outdated
Copy link
Copy Markdown
Contributor

@damskii9992 damskii9992 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good. I have some questions/potential changes before approval.
Also, do we want to raise an error or a warning when a fit does not converge withing max_iterations?

Comment thread src/easyscience/fitting/minimizers/minimizer_bumps.py
Comment thread src/easyscience/fitting/minimizers/minimizer_dfo.py
if getattr(results, name, False):
setattr(results, name, value)
results.success = not bool(fit_results.flag)
results.success = fit_results.flag == fit_results.EXIT_SUCCESS
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this? What is fit_results.EXIT_SUCCESS?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And should we raise a warning if the fit did not succeed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In DFO-LS, EXIT_SUCCESS is 0 when “objective is sufficiently small” or “rho has reached rhoend”. Positive codes are warning exits, and negative codes are hard errors.

whether to raise a warning: not for every non-success. Hard failures already become an exception in minimizer_dfo, so warning on those would be redundant. But for warning-style exits that still return results, especially EXIT_MAXFUN_WARNING, adding a UserWarning would be reasonable if we want consistency with BUMPS, which warns when it hits the evaluation budget.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added warning and unit test


if 'Success' not in results.msg:
raise FitError(f'Fit failed with message: {results.msg}')
if results.flag in {results.EXIT_SUCCESS, results.EXIT_MAXFUN_WARNING}:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also explain this? Maybe add a comment?

except FitError:
for key in self._cached_pars.keys():
self._cached_pars[key].value = self._cached_pars_vals[key][0]
raise
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codecov says this misses a test :)

results.y_calc = fit_results.best_fit
results.y_err = 1 / fit_results.weights
results.n_evaluations = fit_results.nfev
results.message = fit_results.message
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably either raise an error or a warning when max_evaluations have been reached.


assert result.success is False
assert result.n_evaluations is not None
assert result.n_evaluations > 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the fit reached max evaluations, shouldn't this be:

assert result.n_evaluations == 3

assert domain_fit_results.y_calc == 'evaluate'
assert domain_fit_results.y_err == 'dy'
assert domain_fit_results.n_evaluations == 7
assert domain_fit_results.message == 'Fit stopped: reached maximum evaluations (3)'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh? max evaluations was 3 but it got 7 iterations? What?

@rozyczko
Copy link
Copy Markdown
Member Author

rozyczko commented Apr 21, 2026

Generally looks good. I have some questions/potential changes before approval. Also, do we want to raise an error or a warning when a fit does not converge withing max_iterations?

I would prefer to just emit the warning and let the caller check the results object for convergence.
Or maybe we should all have a discussion about that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[priority] medium Normal/default priority [scope] enhancement Adds/improves features (major.MINOR.patch)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants